#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
GIF文件解析器
支持解析GIF文件的基本结构和信息

GIF 字节格式介绍
https://www.burqee.com/article/11/
"""

import struct
import os
from typing import Dict, List, Tuple, Optional

class GIFParser:
    """GIF文件解析器类"""
    
    def __init__(self, file_path: str):
        self.file_path = file_path
        self.file_size = 0
        self.header = {}
        self.logical_screen_descriptor = {}
        self.global_color_table = []
        self.images = []
        self.extensions = []
        
    def parse(self) -> bool:
        """解析GIF文件"""
        try:
            with open(self.file_path, 'rb') as f:
                self.file_size = os.path.getsize(self.file_path)
                print(f"开始解析GIF文件: {self.file_path}")
                print(f"文件大小: {self.file_size} 字节")
                print("-" * 50)
                
                # 解析GIF文件头
                if not self._parse_header(f):
                    return False
                
                # 解析逻辑屏幕描述符
                if not self._parse_logical_screen_descriptor(f):
                    return False
                
                # 解析全局颜色表（如果存在）
                if self.logical_screen_descriptor['global_color_table_flag']:
                    self._parse_global_color_table(f)
                
                # 解析数据流
                self._parse_data_stream(f)
                
                return True
                
        except Exception as e:
            print(f"解析GIF文件时出错: {e}")
            return False
    
    def _parse_header(self, f) -> bool:
        """解析GIF文件头"""
        header_data = f.read(6)
        if len(header_data) != 6:
            print("错误: 文件头长度不足")
            return False
        
        signature = header_data[:3].decode('ascii')
        version = header_data[3:6].decode('ascii')
        
        if signature != 'GIF':
            print(f"错误: 不是有效的GIF文件，签名: {signature}")
            return False
        
        self.header = {
            'signature': signature,
            'version': version
        }
        
        print(f"GIF签名: {signature}")
        print(f"版本: {version}")
        return True
    
    def _parse_logical_screen_descriptor(self, f) -> bool:
        """解析逻辑屏幕描述符"""
        descriptor_data = f.read(7)
        if len(descriptor_data) != 7:
            print("错误: 逻辑屏幕描述符长度不足")
            return False
        
        # 解析宽度和高度（小端序）
        width = struct.unpack('<H', descriptor_data[0:2])[0]
        height = struct.unpack('<H', descriptor_data[2:4])[0]
        
        # 解析打包字段
        packed = descriptor_data[4]
        global_color_table_flag = (packed & 0x80) != 0
        color_resolution = ((packed & 0x70) >> 4) + 1
        sort_flag = (packed & 0x08) != 0
        global_color_table_size = 2 ** ((packed & 0x07) + 1)
        
        # 背景色索引
        background_color_index = descriptor_data[5]
        
        # 像素宽高比
        pixel_aspect_ratio = descriptor_data[6]
        
        self.logical_screen_descriptor = {
            'width': width,
            'height': height,
            'global_color_table_flag': global_color_table_flag,
            'color_resolution': color_resolution,
            'sort_flag': sort_flag,
            'global_color_table_size': global_color_table_size,
            'background_color_index': background_color_index,
            'pixel_aspect_ratio': pixel_aspect_ratio
        }
        
        print(f"逻辑屏幕尺寸: {width} x {height}")
        print(f"全局颜色表: {'是' if global_color_table_flag else '否'}")
        if global_color_table_flag:
            print(f"颜色表大小: {global_color_table_size} 种颜色")
        print(f"颜色分辨率: {color_resolution} 位")
        print(f"背景色索引: {background_color_index}")
        
        return True
    
    def _parse_global_color_table(self, f):
        """解析全局颜色表"""
        table_size = self.logical_screen_descriptor['global_color_table_size']
        color_data = f.read(table_size * 3)
        
        self.global_color_table = []
        for i in range(table_size):
            r = color_data[i * 3]
            g = color_data[i * 3 + 1]
            b = color_data[i * 3 + 2]
            self.global_color_table.append((r, g, b))
        
        print(f"全局颜色表已解析，包含 {len(self.global_color_table)} 种颜色")
    
    def _parse_data_stream(self, f):
        """解析数据流（图像、扩展等）"""
        while True:
            # 读取分隔符
            separator = f.read(1)
            if not separator:
                break
            
            separator_byte = separator[0]
            
            if separator_byte == 0x2C:  # 图像分隔符
                self._parse_image_descriptor(f)
            elif separator_byte == 0x21:  # 扩展分隔符
                self._parse_extension(f)
            elif separator_byte == 0x3B:  # 文件结束符
                print("到达文件结束符")
                break
            else:
                print(f"未知分隔符: 0x{separator_byte:02X}")
                break
    
    def _parse_image_descriptor(self, f):
        """解析图像描述符"""
        descriptor_data = f.read(9)
        if len(descriptor_data) != 9:
            print("错误: 图像描述符长度不足")
            return
        
        # 解析图像位置和尺寸
        left = struct.unpack('<H', descriptor_data[0:2])[0]
        top = struct.unpack('<H', descriptor_data[2:4])[0]
        width = struct.unpack('<H', descriptor_data[4:6])[0]
        height = struct.unpack('<H', descriptor_data[6:8])[0]
        
        # 解析打包字段
        packed = descriptor_data[8]
        local_color_table_flag = (packed & 0x80) != 0
        interlace_flag = (packed & 0x40) != 0
        sort_flag = (packed & 0x20) != 0
        local_color_table_size = 2 ** ((packed & 0x07) + 1)
        
        image_info = {
            'left': left,
            'top': top,
            'width': width,
            'height': height,
            'local_color_table_flag': local_color_table_flag,
            'interlace_flag': interlace_flag,
            'sort_flag': sort_flag,
            'local_color_table_size': local_color_table_size
        }
        
        print(f"图像: 位置({left}, {top}), 尺寸 {width}x{height}")
        print(f"  本地颜色表: {'是' if local_color_table_flag else '否'}")
        print(f"  交错: {'是' if interlace_flag else '否'}")
        
        # 解析本地颜色表（如果存在）
        local_color_table = []
        if local_color_table_flag:
            table_size = local_color_table_size
            color_data = f.read(table_size * 3)
            for i in range(table_size):
                r = color_data[i * 3]
                g = color_data[i * 3 + 1]
                b = color_data[i * 3 + 2]
                local_color_table.append((r, g, b))
            print(f"  本地颜色表大小: {len(local_color_table)} 种颜色")
        
        # 解析LZW最小代码长度
        lzw_min_code_length = f.read(1)[0]
        print(f"  LZW最小代码长度: {lzw_min_code_length}")
        
        # 读取图像数据（LZW压缩数据）
        image_data = self._read_image_data(f)
        
        image_info['local_color_table'] = local_color_table
        image_info['lzw_min_code_length'] = lzw_min_code_length
        image_info['image_data'] = image_data
        self.images.append(image_info)
    
    def _skip_image_data(self, f):
        """跳过图像数据块"""
        while True:
            block_size = f.read(1)[0]
            if block_size == 0:
                break
            f.read(block_size)
    
    def _read_image_data(self, f):
        """读取图像数据块"""
        data = bytearray()
        while True:
            block_size = f.read(1)[0]
            if block_size == 0:
                break
            data.extend(f.read(block_size))
        return bytes(data)
    
    def _lzw_decompress(self, data, min_code_size):
        """LZW解压缩算法"""
        if not data:
            return []
        
        # 初始化字典
        clear_code = 1 << min_code_size
        end_code = clear_code + 1
        next_code = end_code + 1
        
        # 构建初始字典
        dictionary = {}
        for i in range(clear_code):
            dictionary[i] = [i]
        
        # 解压缩
        result = []
        code_size = min_code_size + 1
        max_code = (1 << code_size) - 1
        
        # 读取第一个代码
        bit_buffer = 0
        bit_count = 0
        data_index = 0
        
        def read_code():
            nonlocal bit_buffer, bit_count, data_index
            while bit_count < code_size:
                if data_index >= len(data):
                    return None
                bit_buffer |= data[data_index] << bit_count
                bit_count += 8
                data_index += 1
            
            code = bit_buffer & ((1 << code_size) - 1)
            bit_buffer >>= code_size
            bit_count -= code_size
            return code
        
        # 读取第一个代码
        old_code = read_code()
        if old_code is None:
            return []
        
        if old_code == clear_code:
            return []
        
        if old_code == end_code:
            return []
        
        result.extend(dictionary[old_code])
        
        # 解压缩循环
        while True:
            new_code = read_code()
            if new_code is None:
                break
            
            if new_code == end_code:
                break
            
            if new_code == clear_code:
                # 重置字典
                dictionary = {}
                for i in range(clear_code):
                    dictionary[i] = [i]
                next_code = end_code + 1
                code_size = min_code_size + 1
                max_code = (1 << code_size) - 1
                old_code = read_code()
                if old_code is None or old_code == end_code:
                    break
                result.extend(dictionary[old_code])
                continue
            
            if new_code in dictionary:
                entry = dictionary[new_code]
            else:
                entry = dictionary[old_code] + [dictionary[old_code][0]]
            
            result.extend(entry)
            
            # 添加新条目到字典
            if next_code <= 4095:  # 最大字典大小
                dictionary[next_code] = dictionary[old_code] + [entry[0]]
                next_code += 1
                
                # 增加代码大小
                if next_code > max_code and code_size < 12:
                    code_size += 1
                    max_code = (1 << code_size) - 1
            
            old_code = new_code
        
        return result
    
    def _parse_extension(self, f):
        """解析扩展块"""
        extension_label = f.read(1)[0]
        print(f"扩展块标签: 0x{extension_label:02X}")
        
        # 跳过扩展数据
        while True:
            block_size = f.read(1)[0]
            if block_size == 0:
                break
            f.read(block_size)
    
    def print_summary(self):
        """打印GIF文件摘要信息"""
        print("\n" + "=" * 50)
        print("GIF文件解析摘要")
        print("=" * 50)
        print(f"文件: {self.file_path}")
        print(f"大小: {self.file_size} 字节")
        print(f"版本: {self.header['version']}")
        print(f"逻辑屏幕: {self.logical_screen_descriptor['width']} x {self.logical_screen_descriptor['height']}")
        
        if self.global_color_table:
            print(f"全局颜色表: {len(self.global_color_table)} 种颜色")
        
        print(f"图像数量: {len(self.images)}")
        for i, img in enumerate(self.images):
            print(f"  图像 {i+1}: {img['width']} x {img['height']} 在位置 ({img['left']}, {img['top']})")
            if img['local_color_table']:
                print(f"    本地颜色表: {len(img['local_color_table'])} 种颜色")
        
        print(f"扩展块数量: {len(self.extensions)}")
    
    def output_first_frame(self, output_file="first_frame.ppm"):
        """输出第一帧图像为PPM格式"""
        if not self.images:
            print("错误: 没有找到图像数据")
            return False
        
        first_frame = self.images[0]
        
        # 获取颜色表
        color_table = first_frame['local_color_table'] if first_frame['local_color_table'] else self.global_color_table
        
        if not color_table:
            print("错误: 没有找到颜色表")
            return False
        
        # 解压缩图像数据
        print(f"正在解压缩第一帧图像数据...")
        pixel_data = self._lzw_decompress(first_frame['image_data'], first_frame['lzw_min_code_length'])
        
        if not pixel_data:
            print("错误: 解压缩失败")
            return False
        
        print(f"解压缩完成，像素数量: {len(pixel_data)}")
        print(f"图像尺寸: {first_frame['width']} x {first_frame['height']}")
        
        # 创建PPM文件
        try:
            with open(output_file, 'w') as f:
                # PPM头部
                f.write("P3\n")
                f.write(f"{first_frame['width']} {first_frame['height']}\n")
                f.write("255\n")
                
                # 写入像素数据
                for y in range(first_frame['height']):
                    for x in range(first_frame['width']):
                        pixel_index = y * first_frame['width'] + x
                        if pixel_index < len(pixel_data):
                            color_index = pixel_data[pixel_index]
                            if color_index < len(color_table):
                                r, g, b = color_table[color_index]
                                f.write(f"{r} {g} {b} ")
                            else:
                                f.write("0 0 0 ")  # 黑色作为默认值
                        else:
                            f.write("0 0 0 ")  # 黑色作为默认值
                    f.write("\n")
            
            print(f"第一帧图像已保存为: {output_file}")
            return True
            
        except Exception as e:
            print(f"保存图像时出错: {e}")
            return False


def main():
    """主函数"""
    gif_file = "call.gif"
    
    if not os.path.exists(gif_file):
        print(f"错误: 文件 {gif_file} 不存在")
        return
    
    parser = GIFParser(gif_file)
    if parser.parse():
        parser.print_summary()
        print("\n" + "=" * 50)
        print("输出第一帧图像")
        print("=" * 50)
        parser.output_first_frame()
    else:
        print("解析失败")


if __name__ == "__main__":
    main()
